
#include "driver/gpio.h"
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
// #include "protocol_examples_common.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>

#define GPIO_MOTA_A 1
#define GPIO_SETMOTA_A (1 << GPIO_MOTA_A)

#define GPIO_KEY (2)
#define GPIO_KEY_SET (1ULL << GPIO_KEY)

#define WIFI_SSID "asdzxcqwe"
#define WIFI_PASSWORD "123456789"

void vGPIO_Init()
{
    /*GPIO初始化的结构体*/
    gpio_config_t xIO_Conf;

    /*禁止中断*/
    xIO_Conf.intr_type = GPIO_INTR_DISABLE;
    /*输出模式*/
    xIO_Conf.mode = GPIO_MODE_OUTPUT;
    /*配置IO (注意这个是bit位)*/
    xIO_Conf.pin_bit_mask = GPIO_SETMOTA_A;
    /*禁止上拉*/
    xIO_Conf.pull_down_en = 0;
    /*禁止下拉*/
    xIO_Conf.pull_up_en = 0;

    /*配置写入寄存器*/
    gpio_config(&xIO_Conf);
    gpio_set_level(GPIO_MOTA_A, 0);

    xIO_Conf.intr_type = GPIO_INTR_DISABLE;
    xIO_Conf.mode = GPIO_MODE_INPUT;
    xIO_Conf.pin_bit_mask = GPIO_KEY_SET;
    /*禁止上拉*/
    xIO_Conf.pull_down_en = 0;
    /*禁止下拉*/
    xIO_Conf.pull_up_en = 1;

    /*配置写入寄存器*/
    gpio_config(&xIO_Conf);
}

void vMOTOR_Disply(uint32_t SETTIN) // 100US
{
    printf("SETTIN = %ld\r\n", SETTIN);
    for (int i = 0; i < 25; i++)
    {

        gpio_set_level(GPIO_MOTA_A, 1);
        esp_rom_delay_us(SETTIN);
        gpio_set_level(GPIO_MOTA_A, 0);
        esp_rom_delay_us(20 * 1000 - SETTIN);
    }
}

uint8_t retryCount = 0;

void esp_event_handler(void *event_handler,
                       esp_event_base_t event_base,
                       int32_t event_id,
                       void *event_data)
{

    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        printf("start connect wifi ap....\n");
        esp_wifi_connect();
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        printf("retry connect wifi ap...\n");
        if (retryCount < 10)
        {
            esp_wifi_connect();
            retryCount++;
        }
        else
        {

            printf("conncet ap failed\n");
        }
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        retryCount = 0;
        ip_event_got_ip_t *data = (ip_event_got_ip_t *)event_data;
        printf("got ip=%d.%d.%d.%d\n", IP2STR(&data->ip_info.ip));
    }
}

// 联网相关的宏
#define EXAMPLE_ESP_WIFI_SSID "HUAWEI-J1CB44" // 账号
#define EXAMPLE_ESP_WIFI_PASS "123456789"     // 密码
#define EXAMPLE_ESP_MAXIMUM_RETRY 5           // wifi连接失败以后可以重新连接的次数
#define WIFI_CONNECTED_BIT BIT0               // wifi连接成功标志位
#define WIFI_FAIL_BIT BIT1                    // wifi连接失败标志位

static const char *TAG = "image wifi station";

// 联网相关的变量
static EventGroupHandle_t s_wifi_event_group; // 事件组，用于对wifi响应结果进行标记
static int s_retry_num = 0;                   // 记录wifi重新连接尝试的次数

/**
 * @description: 处理wifi连接和ip分配时候事件的回调函数
 * @return {*}
 * @note:
 */
static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    // 如果是wifi station开始连接事件，就尝试将station连接到AP
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        esp_wifi_connect();
    }
    // 如果是wifi station从AP断连事件
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        // 如果没有达到最高尝试次数，继续尝试
        if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
        {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        }
        // 如果达到了最高尝试次数，就标记连接失败
        else
        {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG, "connect to the AP fail");
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); // 成功获取到了ip，就标记这次wifi连接成功
    }
}

/**
 * @description: 用于连接wifi的函数
 * @return {*}
 * @note: 这里wifi连接选项设置了使用nvs，会把每次配置的参数存储在nvs中。因此请查看分区表中是否对nvs分区进行了设置
 */
void wifi_init_sta(void)
{
    // 00 创建wifi事件组
    s_wifi_event_group = xEventGroupCreate();

    /******************** 01 Wi-Fi/LwIP 初始化阶段 ********************/
    // 01-1 创建LWIP核心任务
    ESP_ERROR_CHECK(esp_netif_init());

    // 01-2 创建系统事件任务，并初始化应用程序事件的回调函数
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    // 01-3 创建有 TCP/IP 堆栈的默认网络接口实例绑定 station
    esp_netif_create_default_wifi_sta();

    // 01-4 创建wifi驱动程序任务，并初始化wifi驱动程序
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 01-5 注册，用于处理wifi连接的过程中的事件
    esp_event_handler_instance_t instance_any_id; // 用于处理wifi连接时候的事件的句柄
    esp_event_handler_instance_t instance_got_ip; // 用于处理ip分配时候产生的事件的句柄
    // 该句柄对wifi连接所有事件都产生响应，连接到event_handler回调函数
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    // 该句柄仅仅处理IP_EVENT事件组中的从AP中获取ip地址事件，连接到event_handler回调函数
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    /******************** 02 WIFI配置阶段 ********************/
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
            .threshold.authmode = WIFI_AUTH_WPA2_PSK, // 设置快速扫描模式下能接受的最弱的验证模式
            .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,         // 设置SAE和PWE(wifi协议)的配置
        },
    };
    // 02-2 配置station工作模式
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    // 02-3 配置
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

    /******************** 03 wifi启动阶段 ********************/
    // 03-1 启动wifi驱动程序
    ESP_ERROR_CHECK(esp_wifi_start()); // 会触发回调函数

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    /******************** 输出wifi连接结果 ********************/
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT)
    {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    }
    else if (bits & WIFI_FAIL_BIT)
    {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    }
    else
    {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    /* The event will not be processed after unregister */
    // 05 事件注销
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    vEventGroupDelete(s_wifi_event_group);
}

void WIFI_Init()
{
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    // 连wifi
    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_init_sta();
}


#define PORT 3333
#define MAX_CONNECTIONS 5
#define BUF_SIZE 1024
#define HEARTBEAT_INTERVAL 5000 // 5 seconds

static void handle_connection(int sock) {
    char rx_buffer[BUF_SIZE];
    char addr_str[128];

    while (1) {
        // Receive data from client
        int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0) {
            ESP_LOGE(TAG, "Error occurred during receiving data: errno %d", errno);
            break;
        } else if (len == 0) {
            ESP_LOGW(TAG, "Client disconnected");
            break;
        } else {
            // Ensure null-terminated string
            rx_buffer[len] = 0;
            ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);

            // Check if received data is the unique identifier
            if (strcmp(rx_buffer, "unique_identifier") == 0) {
                // Get local IP address
                esp_netif_ip_info_t ip_info;
                esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info);
                snprintf(addr_str, sizeof(addr_str), "Server IP: %s", ip4addr_ntoa(&ip_info.ip));
                ESP_LOGI(TAG, "Sending server IP address to client");
                send(sock, addr_str, strlen(addr_str), 0);
            } else {
                // Data processing
                // Add your data processing code here
                vMOTOR_Disply(900);
                // vTaskDelay(1000 / portTICK_PERIOD_MS);
                vMOTOR_Disply(500);
                // vTaskDelay(1000 / portTICK_PERIOD_MS);
                ESP_LOGI(TAG, "Processing data...");
                // Echo back received data
                send(sock, "OK\r\n", sizeof("OK\r\n"), 0);
            }
        }
    }

    // Close the socket after processing
    shutdown(sock, SHUT_RDWR);
    close(sock);
}

static void tcp_server_task(void *pvParameters) {
    char addr_str[128];
    int addr_family;
    int ip_protocol;

    struct sockaddr_in6 dest_addr;
    bzero(&dest_addr, sizeof(dest_addr));
    dest_addr.sin6_family = AF_INET6;
    inet6_aton("::", &dest_addr.sin6_addr);
    dest_addr.sin6_port = htons(PORT);
    addr_family = AF_INET6;
    ip_protocol = IPPROTO_IPV6;

    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }

    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0) {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }
    ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    err = listen(listen_sock, MAX_CONNECTIONS);
    if (err != 0) {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }

    while (1) {
        struct sockaddr_in6 source_addr;
        uint addr_len = sizeof(source_addr);
        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        } else {
            // Set socket timeout
            struct timeval timeout;
            timeout.tv_sec = 10; // 10 seconds
            timeout.tv_usec = 0;
            setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));

            // Handle connection
            handle_connection(sock);
        }
    }

    vTaskDelete(NULL);
}


void app_main(void)
{
    vGPIO_Init();
    WIFI_Init();
    vTaskDelay(100 / portTICK_PERIOD_MS);
    vMOTOR_Disply(500);
    // Create the task that will handle the UDP server
     xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 5, NULL);

    while (1)
    {
        if (!gpio_get_level(GPIO_KEY))
        {
            esp_rom_delay_us(500);
            if (!gpio_get_level(GPIO_KEY))
            {
                vMOTOR_Disply(900);
                // vTaskDelay(1000 / portTICK_PERIOD_MS);
                vMOTOR_Disply(500);
                // vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
            while (!gpio_get_level(GPIO_KEY))
                ;
        }
    }
}
